﻿using System;
using System.Net;
using System.Net.Sockets;

namespace RawSocketEx
{
    public class RawSocket
    {
        #region Delegates

        public delegate void PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args);

        #endregion

        public bool KeepRunning; //是否继续进行
        private bool _errorOccurred; //套接字在接收包时是否产生错误
        private SocketAsyncEventArgsEx _iar;
        private Socket _socket; //声明套接字

        public RawSocket() //构造函数
        {
            _errorOccurred = false;
        }

        public event PacketArrivedEventHandler PacketArrival; //声明事件句柄函数

        protected IPAddress GetLocalIp()   //获取本地IP   
        {
            var ipHost = Dns.GetHostEntry(Dns.GetHostName());
            return ipHost.AddressList[0];
        }

        public void CreateAndBindSocket() //建立并绑定套接字
        {
            _iar = new SocketAsyncEventArgsEx();
            _iar.Completed += Receive;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP) { Blocking = false };
            _socket.Bind(new IPEndPoint(GetLocalIp(), 0)); //绑定套接字
            if (!SetSocketOption())
            {
                _errorOccurred = true;
            }
        }

        private bool SetSocketOption() //设置raw socket
        {
            bool retValue = true;
            try
            {
                _socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
                var valutToSet = BitConverter.GetBytes(1);
                var resultReturned = new byte[4];

                //低级别操作模式,接受所有的数据包，这一步是关键，必须把socket设成raw和IP Level才可用　　SIO_RCVALL
                _socket.IOControl(IOControlCode.ReceiveAll, valutToSet, resultReturned);
                var retCode = BitConverter.ToUInt32(resultReturned, 0);
                //把4个8位字节合成一个32位整数
                if (retCode != 0) retValue = false;
            }
            catch (SocketException soe)
            {
                retValue = false;
            }
            return retValue;
        }


        //解析接收的数据包，形成PacketArrivedEventArgs事件数据类对象，并引发PacketArrival事件
        private unsafe void Receive(object sender, SocketAsyncEventArgs socketAsyncEventArgs)
        {
            uint tempVersion;
            IPAddress tempIp;

            var e = new PacketArrivedEventArgs(); //新网络数据包信息事件

            fixed (byte* fixedBuf = socketAsyncEventArgs.Buffer)
            {
                var head = (IpHeader*)fixedBuf; //把数据流整和为IpHeader结构
                e.HeaderLength = (uint)(head->ip_verlen & 0x0F) << 2;

                byte tempProtocol = head->ip_protocol;
                switch (tempProtocol) //提取协议类型
                {
                    case 1:
                        e.Protocol = "ICMP";
                        break;
                    case 2:
                        e.Protocol = "IGMP";
                        break;
                    case 6:
                        e.Protocol = "TCP";
                        break;
                    case 17:
                        e.Protocol = "UDP";
                        break;
                    default:
                        e.Protocol = "UNKNOWN";
                        break;
                }

                tempVersion = (uint)(head->ip_verlen & 0xF0) >> 4; //提取IP协议版本
                e.IpVersion = tempVersion.ToString();

                //以下语句提取出了PacketArrivedEventArgs对象中的其他参数
                uint tempIpSrcaddr = head->ip_srcaddr;
                uint tempIpDestaddr = head->ip_destaddr;
                tempIp = new IPAddress(tempIpSrcaddr);
                e.OriginationAddress = tempIp.ToString();
                tempIp = new IPAddress(tempIpDestaddr);
                e.DestinationAddress = tempIp.ToString();

                short tempSrcport = *(short*)&fixedBuf[e.HeaderLength];
                short tempDstport = *(short*)&fixedBuf[e.HeaderLength + 2];
                e.OriginationPort = IPAddress.NetworkToHostOrder(tempSrcport).ToString();
                e.DestinationPort = IPAddress.NetworkToHostOrder(tempDstport).ToString();

                e.PacketLength = (uint)socketAsyncEventArgs.BytesTransferred;
                e.MessageLength = (uint)socketAsyncEventArgs.BytesTransferred - e.HeaderLength;

                //e.ReceiveBuffer = buf;
                //把buf中的IP头赋给PacketArrivedEventArgs中的IpHeaderBuffer
                //Array.Copy(buf, 0, e.IpHeaderBuffer, 0, (int)e.HeaderLength);
                //把buf中的包中内容赋给PacketArrivedEventArgs中的MessageBuffer
                //Array.Copy(buf, (int)e.HeaderLength, e.MessageBuffer, 0, (int)e.MessageLength);
            }

            //引发PacketArrival事件
            OnPacketArrival(e);

            if (KeepRunning && !_errorOccurred)
            {
                _errorOccurred = !_socket.ReceiveAsync(_iar);
            }
            else
            {
                _socket.Shutdown(SocketShutdown.Both);
                _socket.Close();
                _socket = null;
            }
        }

        private void OnPacketArrival(PacketArrivedEventArgs packetArrivedEventArgs)
        {
            if (PacketArrival != null)
            {
                PacketArrival(this, packetArrivedEventArgs);
            }
        }

        public void Run() //开始监听
        {
            if (_socket == null)
            {
                CreateAndBindSocket();
            }
            KeepRunning = true;
            _errorOccurred = !_socket.ReceiveAsync(_iar);
        }

        public void Shutdown() //关闭raw socket
        {
            KeepRunning = false;
        }
    }
}